package org.micode.common.config;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import org.micode.common.utils.StringExtUtils;
import org.micode.common.config.reader.ConfigReaderFactory;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;

import java.math.BigInteger;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.List;

/**
 * 应用程序的配置信息，所有配置都在这里缓存，用到的地方从这获取，如果有修改时，会刷新这里的配置缓存。
 * 每个应用程序应该有如下配置值：
 *     # 应用程序名称
 *     spring.application.name
 *     # 配置信息读取器类名(含包的全名)
 *     spring.application.config.reader
 */
@Slf4j
public class ApplicationConfig {

    protected List<String> testFlags = new ArrayList<>();

    public static String MODULE_ID;

    public static String REMOTE_SERVICE_PACKAGE = "com.micode";

    /**
     * 扫描远程服务接口的包
     */
    public static String getRemoteServicePackage() {
        return REMOTE_SERVICE_PACKAGE;
    }

    @Value("${remote.service.package:}")
    public void setRemoteServicePackage(String remoteServicePackage) {
        if (StringExtUtils.isAnyEmpty(remoteServicePackage)) {
            REMOTE_SERVICE_PACKAGE = "com.micode";
        } else {
            REMOTE_SERVICE_PACKAGE = remoteServicePackage;
        }
    }

    @Value("${spring.application.id:800}")
    public void setModuleId(String moduleId) {
        MODULE_ID = moduleId;
    }

    /**
     * 服务名
     */
    public static String SERVICE_NAME;

    /**
     * 配置信息读取器类名称
     */
    private static String CONFIG_READER;

    @Value("${server.test_flag:}")
    public void setTestFlag(String testFlag) {
        testFlags.clear();

        if (null == testFlag) {
            return;
        }

        testFlags.addAll(Arrays.asList(testFlag.split("\\|")));
    }

    @Value("${spring.application.config.reader:}")
    public void setConfigReader(String configReader) {
        ApplicationConfig.CONFIG_READER = configReader;
    }

    @Value("${spring.application.name:unknown}")
    public void setServiceName(String serviceName) {
        ApplicationConfig.SERVICE_NAME = serviceName;
    }

    public static void reloadAll() {
        ConfigReaderFactory.configReader(CONFIG_READER).loadAll();
    }

    public static String readString(String name, String defaultValue) {
        return readValue(name, defaultValue, String.class);
    }

    public static Integer readInteger(String name, Integer defaultValue) {
        return readValue(name, defaultValue, Integer.class);
    }

    public static Long readLong(String name, Long defaultValue) {
        return readValue(name, defaultValue, Long.class);
    }

    public static Boolean readBoolean(String name, Boolean defaultValue) {
        return readValue(name, defaultValue, Boolean.class);
    }

    private static <T> T readValue(String name, T defaultValue, Class<T> clazz) {
        String value = ConfigReaderFactory.configReader(CONFIG_READER)
                .readProperty(name, "");
        if (null == value || StringUtils.isEmpty(value)) {
            return defaultValue;
        }
        String val = value.trim().toLowerCase();

        Object ret;
        if (clazz.equals(Boolean.class)) {
            if (val.equals("true")) {
                ret = Boolean.TRUE;
            } else {
                ret = Boolean.FALSE;
            }
            return (T) ret;
        } else if (clazz.equals(Integer.class)) {
            try {
                ret = Integer.valueOf(val);
            } catch (Exception e) {
                log.warn("readValue() fail，{}", e.getMessage());
                ret = defaultValue;
            }
            return (T) ret;
        } else if (clazz.equals(Long.class)) {
            try {
                ret = Long.valueOf(val);
            } catch (Exception e) {
                log.warn("readValue() fail for Long value，{}", e.getMessage());
                ret = defaultValue;
            }
            return (T) ret;
        } else {
            return (T) value;
        }
    }

    public String getLocalIp() {
        try {
            String hostAddress = null;
            Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
            while (networkInterfaces.hasMoreElements()) {
                NetworkInterface networkInterface = networkInterfaces.nextElement();
                if (networkInterface.getName().equals("lo")) {
                    continue;
                }
                Enumeration<InetAddress> addresses = networkInterface.getInetAddresses();
                while (addresses.hasMoreElements()) {
                    InetAddress inetAddress = addresses.nextElement();
                    if (inetAddress instanceof Inet4Address) {
                        hostAddress = inetAddress.getHostAddress();
                        break;
                    }
                }
                if (hostAddress != null) {
                    break;
                }
            }
            return hostAddress;
        } catch(Exception e) {
            return "";
        }
    }

    @Bean
    public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
        return jacksonObjectMapperBuilder -> jacksonObjectMapperBuilder
                .serializerByType(Long.class, ToStringSerializer.instance)
                .serializerByType(Long.TYPE, ToStringSerializer.instance)
                .serializerByType(BigInteger.class, ToStringSerializer.instance)
                .failOnUnknownProperties(false);
//                .serializationInclusion(JsonInclude.Include.NON_NULL);
    }

    private static ObjectMapper DEFAULT_OBJECT_MAPPER =
            new ObjectMapper()
                    .registerModule(
                            new SimpleModule()
                                    .addSerializer(Long.TYPE, ToStringSerializer.instance)
                                    .addSerializer(Long.class, ToStringSerializer.instance)
                                    .addSerializer(BigInteger.class, ToStringSerializer.instance))
                    .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
                    .configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true);
//                    .setSerializationInclusion(JsonInclude.Include.NON_NULL);

    public static String objectToString(Object obj) {
        if (null == obj) {
            return null;
        }

        try {
            return DEFAULT_OBJECT_MAPPER.writeValueAsString(obj);
        } catch (JsonProcessingException e) {
            return obj.toString();
        }
    }



}
